package com.cheer.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.date.DateUtil;
import com.cheer.dao.*;
import com.cheer.dto.AccountDTO;
import com.cheer.dto.ForgetPasswordDTO;
import com.cheer.dto.RegisterDTO;
import com.cheer.entity.Account;
import com.cheer.entity.Admin;
import com.cheer.entity.Applicant;
import com.cheer.entity.Organization;
import com.cheer.enums.*;
import com.cheer.exception.ServiceException;
import com.cheer.manager.AccountManager;
import com.cheer.security.SecurityConfig;
import com.cheer.service.AuthService;
import com.cheer.vo.AccountVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Date;

/**
 * @author cheer
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    private static final int CAPTCHA_WIDTH = 200;
    private static final int CAPTCHA_HEIGHT = 100;
    private static final int CAPTCHA_COUNT = 4;
    private static final int CAPTCHA_LINE = 150;
    private static final String SECURITY_ACCOUNT_ID = "securityAccountId";

    @Autowired
    private ApplicantDao applicantDao;

    @Autowired
    private OrganizationDao organizationDao;

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private AdminDao adminDao;

    @Autowired
    private AccountManager accountManager;

    @Override
    public void getCaptcha(Integer width, Integer height, Integer type) {
        String attribute = CaptchaEnum.typeToAttribute(type);
        if (attribute == null) {
            throw new ServiceException("验证码类型不合法");
        }
        if (width == null) {
            width = CAPTCHA_WIDTH;
        }
        if (height == null) {
            height = CAPTCHA_HEIGHT;
        }

        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(width, height, CAPTCHA_COUNT, CAPTCHA_LINE);
        if (log.isDebugEnabled()) {
            log.info("验证码为 {}", captcha.getCode());
        }

        ServletRequestAttributes servletRequest = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes());
        servletRequest.getRequest().getSession().setAttribute(attribute, captcha.getCode());

        try {
            HttpServletResponse response = servletRequest.getResponse();
            if (response != null) {
                captcha.write(response.getOutputStream());
            }
        } catch (IOException e) {
            log.error("输出验证码异常：" + e.getMessage(), e);
        }
    }

    @Override
    @Transactional
    public void register(RegisterDTO registerDTO) {
        checkCaptcha(registerDTO.getCaptcha(), CaptchaEnum.REGISTER);
        checkRegisterDTO(registerDTO);
        Organization organization = checkOrganization(registerDTO.getOrgId());



        AccountDTO accountDTO = registerDTO.build();
        Long accountId = accountManager.saveAccount(accountDTO);

        Organization joinOrg = null;
        if (registerDTO.getReviewType() == ReviewTypeEnum.JOIN.getCode()){
            joinOrg = checkOrganization(registerDTO.getJoinReviewOrgId());
        }
        Applicant applicant = registerDTO.build(accountId, organization.getOrgPath(), joinOrg);
        applicantDao.insert(applicant);
    }

    @Override
    public String getQuestion(String phoneOrBackup) {
        Account account = accountManager.getByAccount(phoneOrBackup);
        if (account == null) {
            throw new ServiceException("账号不存在或已失效");
        }
        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getSession()
                .setAttribute(SECURITY_ACCOUNT_ID, account.getId());
        return account.getSecurityQuestion();
    }

    @Override
    public void updatePassword(ForgetPasswordDTO forgetPasswordDTO) {
        checkCaptcha(forgetPasswordDTO.getCaptcha(), CaptchaEnum.FORGET_PASSWORD);
        if (!StringUtils.equals(forgetPasswordDTO.getPassword(), forgetPasswordDTO.getConfirmPassword())) {
            throw new ServiceException("两次密码输入不一致");
        }

        HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
                .getRequest().getSession();
        Object accountId = session.getAttribute(SECURITY_ACCOUNT_ID);
        if (accountId == null) {
            throw new ServiceException("会话已过期，请刷新页面重试");
        }

        Account account = accountDao.selectById((Long) accountId);
        if (account == null || account.getLocked()) {
            throw new ServiceException("账号已失效");
        }
        if (!StringUtils.equals(forgetPasswordDTO.getAnswer(), account.getSecurityAnswer())) {
            throw new ServiceException("答案填写不正确");
        }

        accountManager.updatePassword((Long) accountId, forgetPasswordDTO.getPassword());
        session.removeAttribute(SECURITY_ACCOUNT_ID);
    }

    @Override
    public Boolean isLoginCaptchaRequired() {
        HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
                .getRequest().getSession();
        return session.getAttribute(SecurityConfig.FAILED_LOGIN) != null;
    }

    @Override
    public AccountVO getAccount(Long accountId) {
        // 查询账户
        Account account = accountDao.selectById(accountId);
        if (account == null) {
            throw new ServiceException("账号已失效");
        }
        // 拼装数据
        AccountVO accountVO = AccountVO.build(account);
        if (account.getRole() == RoleEnum.APPLICANT.getCode()) {
            Applicant applicant = applicantDao.getByAccountId(accountId);
            if (applicant != null) {
                accountVO.setAccountName(applicant.getContactPerson());
                accountVO.setTitle(applicant.getTitle());
            }
        } else {
            Admin admin = adminDao.getByAccountId(accountId);
            if (admin != null) {
                accountVO.setAccountName(admin.getContactPerson());
            }
        }
        return accountVO;
    }

    /**
     * 验证码校验
     *
     * @param captcha     输入验证码
     * @param captchaEnum 验证码类型
     */
    private void checkCaptcha(String captcha, CaptchaEnum captchaEnum) {
        HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getSession();
        Object object = session.getAttribute(captchaEnum.getAttribute());
        if (object == null) {
            throw new ServiceException("验证码已失效");
        }
        try {
            String code = (String) object;
            if (!StringUtils.equals(code, captcha)) {
                throw new ServiceException("验证码不正确");
            }
        } finally {
            session.removeAttribute(captchaEnum.getAttribute());
        }
    }

    /**
     * 校验注册参数
     *
     * @param registerDTO 注册信息
     */
    private void checkRegisterDTO(RegisterDTO registerDTO) {
        if (!StringUtils.equals(registerDTO.getPassword(), registerDTO.getConfirmPassword())) {
            throw new ServiceException("两次密码输入不一致");
        }
        if (registerDTO.getYouthNumber() > registerDTO.getPersonNumber()) {
            throw new ServiceException("青年人数不能大于职工人数");
        }
        if (registerDTO.getYouthNumber() * 2 < registerDTO.getPersonNumber()) {
            throw new ServiceException("青年人数比例不能低于 50%");
        }
        if(DateUtil.compare(registerDTO.getContactPersonBirth(), new Date()) > 0){
            throw new ServiceException("号长年龄必须小于当前日期");
        }
        if (DateUtil.ageOfNow(registerDTO.getContactPersonBirth()) >= 40) {
            throw new ServiceException("号长年龄必须小于 40 岁");
        }
        if (registerDTO.getAwarded()) {
            if (registerDTO.getTitle() == null) {
                throw new ServiceException("文明号级别不能为空");
            }
            if (!TitleEnum.isValidTitle(registerDTO.getTitle())) {
                throw new ServiceException("文明号级别不是合法值");
            }
            if (registerDTO.getTitleYear() == null) {
                throw new ServiceException("获得年份不能为空");
            }
            if(Integer.valueOf(registerDTO.getTitleYear()) > DateUtil.thisYear()){
                throw new ServiceException("获得年份不能大于当年");
            }
        }
        if (applicantDao.getByApplicantName(registerDTO.getApplicantName()) != null) {
            throw new ServiceException("集体名称已存在");
        }
    }

    /**
     * 检验组织机构合法性
     *
     * @param orgId 机构Id
     */
    private Organization checkOrganization(Long orgId) {
        Organization organization = organizationDao.selectById(orgId);
        if (organization == null) {
            throw new ServiceException("组织机构不存在");
        }
        return organization;
    }

}
